package com.second.app.thread.线程基础.day07.线程通信.唤醒通知;

import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @Author GYZ
 * @Date 2023/11/27
 * <pre>
 *   线程通信
 *       wait()方法可以使调用该方法的线程释放共享资源的锁，然后从运行状态退出，进入等待队列，直到再次被唤醒。
 *       notify()方法可以随机唤醒等待队列中等待同一个共享资源的”一个“进程，并使该线程退出等待队列，进入可运行状态。
 *               也就是notify方法仅需要通知一个线程。
 *       notifyAll()方法可以随机唤醒等待队列中等待同一个共享资源的”全部“进程，并使该线程退出等待队列，进入可运行状态。
 *  *               此时优先级最高的那个线程最先执行，但也可能是随机执行，因为这要取决于Jvm虚拟机的实现。
 *      案例1：虽说实现了线程通信，但是有个弊端，就是线程B对象不停的通过while语句轮询机制来检测一个条件。
 *            这样会浪费cpu资源.
 *
 *        厨师和服务员,厨师做完一道菜的时间不确定，所以厨师将菜品放到“菜品传递台”上的时间也不确定
 *       服务员如何取到菜，取决于厨师，厨师将菜放到菜品传递台上，其实就相当于一种通知notify,服务员
 *       才可以拿到菜交给就餐者。所以在这个过程出现了“等待/通知”机制。
 *
 *      案例2：线程1内部进行等待wait，此时释放锁,线程2执行，拿到同步锁，唤醒notify线程1。但是线程1不会立马被唤醒，
 *           必须要等到notify包含的代码全部执行完成，线程1才可以被唤醒，继续执行后续。
 *      案例3：
 *           两个线程，线程A判断集合中元素是否等于5，一开始不等于5，所以线程a，进入等待，并释放锁,
 *           线程B往集合中添加元素，当集合中元素个数为5,执行notify，但是必须等待notify全部执行完毕，
 *           线程A得到通知，才能继续执行后续逻辑。
 *      案例4：
 *           当interrupt方法遇到wait方法。
 *           线程A等待，释放锁进入等待队列。5s后，被打断。线程终止，锁也会释放。
 *      案例5：
 *           随机唤醒一个线程（3个线程等待，1个线程负责唤醒）
 *           唤醒所有线程
 *      案例6：
 *           wait(long)等待某一时间内是否有线程对锁进行唤醒，如果超过时间自动唤醒
 *      案例7：
 *         通知过早，会打乱程序正常的运行逻辑。
 *      案例8：
 *          1个生产者和1个消费者
 *          生产者轮询变量值，如果变量值不为空，线程wait。为空，生产值，通知消费者消费。
 *          消费则轮询变量值，如果值为空，则线程wait。不为空，消费值，并将值置为空，再通知生产者生产。
 *      案例9：
 *        多生产多消费，操作值-假死
 *        解决方案：notifyAll()，不仅要唤醒同类，也要唤醒异类
 * </pre>
 */
//案例9

class Demo9 {
    public static void main(String[] args) {
        class Producer extends Thread {
            private final Object lock;

            public Producer(Object lock) {
                this.lock = lock;
            }

            @Override
            public void run() {
                try {
                    while (true) {
                        synchronized (lock) {
                            if (StringUtils.isNotEmpty(ValueObject.value)) {
                                System.out.println("生产者"+Thread.currentThread().getName()+" WAITING了 ★");
                                lock.wait();
                            }
                            System.out.println("生产者"+Thread.currentThread().getName()+" RUNNABLE了");
                            String value = System.currentTimeMillis() + "_" + System.nanoTime();
                            System.out.println("set 值=" + value);
                            ValueObject.value = value;
                            lock.notify();
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        class Consumer extends Thread {
            private final Object lock;

            public Consumer(Object lock) {
                this.lock = lock;
            }

            @Override
            public void run() {
                try {
                    while (true) {
                        synchronized (lock) {
                            if (StringUtils.isEmpty(ValueObject.value)) {
                                System.out.println("消费者"+Thread.currentThread().getName()+" WAITING了 ☆");
                                lock.wait();
                            }
                            System.out.println("消费者"+Thread.currentThread().getName()+" RUNNABLE了");
                            String value = ValueObject.value;
                            System.out.println("get 值=" + value);
                            ValueObject.value = "";
                            lock.notify();
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        Object lock = new Object();
        for (int i = 0; i < 2; i++) {
            Producer producer = new Producer(lock);
            Consumer consumer = new Consumer(lock);
            producer.start();
            consumer.start();
        }
        try {
            Thread.sleep(5000);
            Thread[] threads = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
            Thread.currentThread().getThreadGroup().enumerate(threads);
            for (int i = 0; i < threads.length; i++) {
                System.out.println(threads[i].getName() + " " + threads[0].getState());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}


//案例8
class ValueObject {
    public static String value = "";
}

class Demo8 {
    public static void main(String[] args) {
        class Producer extends Thread {
            private final Object lock;

            public Producer(Object lock) {
                this.lock = lock;
            }

            @Override
            public void run() {
                try {
                    while (true) {
                        synchronized (lock) {
                            if (StringUtils.isNotEmpty(ValueObject.value)) {
                                lock.wait();
                            }
                            String value = System.currentTimeMillis() + "_" + System.nanoTime();
                            System.out.println("set 值=" + value);
                            ValueObject.value = value;
                            lock.notify();
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        class Consumer extends Thread {
            private final Object lock;

            public Consumer(Object lock) {
                this.lock = lock;
            }

            @Override
            public void run() {
                try {
                    while (true) {
                        synchronized (lock) {
                            if (StringUtils.isEmpty(ValueObject.value)) {
                                lock.wait();
                            }
                            String value = ValueObject.value;
                            System.out.println("get 值=" + value);
                            ValueObject.value = "";
                            lock.notify();
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        try {
            Object lock = new Object();
            Producer producer = new Producer(lock);
            Consumer consumer = new Consumer(lock);
            producer.start();
            Thread.sleep(1000);
            consumer.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

//案例7
class Demo7 {
    public static void main(String[] args) {
        Object lock = new Object();
        AtomicBoolean result = new AtomicBoolean(false);

        Thread threadB = new Thread(() -> {
            synchronized (lock) {
                System.out.println("begin notify");
                lock.notify();
                result.set(true);
                System.out.println("end notify");
            }

        });

        Thread threadA = new Thread(() -> {
            try {
                synchronized (lock) {
                    while (!result.get()) {
                        System.out.println("begin wait");
                        lock.wait();
                        System.out.println("begin end");
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        try {
            //注释1（通知过早）
//            threadB.start();
//            Thread.sleep(100);
//            threadA.start();
            //注释2（正常）
            threadA.start();
            Thread.sleep(100);
            threadB.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

//案例6
class Demo6 {
    public static void main(String[] args) {
        class Thread01 extends Thread {
            private Object lock;

            public Thread01(Object lock) {
                this.lock = lock;
            }

            @Override
            public void run() {
                try {
                    synchronized (lock) {
                        System.out.println("Current ThreadName" + currentThread().getName() + "begin wait=" + System.currentTimeMillis());
                        lock.wait(5000);
                        System.out.println("Current ThreadName" + currentThread().getName() + "end wait=" + System.currentTimeMillis());
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        class Thread02 extends Thread {
            private Object lock;

            public Thread02(Object lock) {
                this.lock = lock;
            }

            @Override
            public void run() {
                synchronized (lock) {
                    lock.notify();
                    System.out.println("发出通知，但是我们发现此时线程A已经自动唤醒了！");
                }
            }
        }
        try {
            Object lock = new Object();
            new Thread01(lock).start();
            Thread.sleep(5000);
            new Thread02(lock).start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


//案例5
class Demo5 {
    public static void main(String[] args) {
        class WaitThread01 extends Thread {
            private final Object lock;

            public WaitThread01(Object lock) {
                this.lock = lock;
            }

            @Override
            public void run() {
                try {
                    synchronized (lock) {
                        System.out.println("Current ThreadName" + currentThread().getName() + "begin wait=" + System.currentTimeMillis());
                        lock.wait();
                        System.out.println("Current ThreadName" + currentThread().getName() + "end wait=" + System.currentTimeMillis());
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        class WaitThread02 extends Thread {
            private final Object lock;

            public WaitThread02(Object lock) {
                this.lock = lock;
            }

            @Override
            public void run() {
                try {
                    synchronized (lock) {
                        System.out.println("Current ThreadName" + currentThread().getName() + "begin wait=" + System.currentTimeMillis());
                        lock.wait();
                        System.out.println("Current ThreadName" + currentThread().getName() + "end wait=" + System.currentTimeMillis());
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        class WaitThread03 extends Thread {
            private final Object lock;

            public WaitThread03(Object lock) {
                this.lock = lock;
            }

            @Override
            public void run() {
                try {
                    synchronized (lock) {
                        System.out.println("Current ThreadName" + currentThread().getName() + "begin wait=" + System.currentTimeMillis());
                        lock.wait();
                        System.out.println("Current ThreadName" + currentThread().getName() + "end wait=" + System.currentTimeMillis());
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        class NotifyThread extends Thread {
            private final Object lock;

            public NotifyThread(Object lock) {
                this.lock = lock;
            }

            @Override
            public void run() {
                synchronized (lock) {
                    try {
//                        lock.notify();
                        lock.notifyAll();
                        Thread.sleep(2000);
                        System.out.println("开始通知了，不知道谁要醒了啊");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        try {
            Object lock = new Object();

            WaitThread01 waitThread01 = new WaitThread01(lock);
            waitThread01.start();

            WaitThread02 waitThread02 = new WaitThread02(lock);
            waitThread02.start();

            WaitThread03 waitThread03 = new WaitThread03(lock);
            waitThread03.start();

            Thread.sleep(5000);
            NotifyThread notifyThread = new NotifyThread(lock);
            notifyThread.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

//案例4
class Demo4 {
    public static void main(String[] args) {
        class Thread01 extends Thread {
            private final Object lock;

            public Thread01(Object lock) {
                this.lock = lock;
            }

            @Override
            public void run() {
                try {
                    synchronized (lock) {
                        System.out.println("wait begin time =" + System.currentTimeMillis());
                        lock.wait();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("出现异常了，因为wait状态的线程被interrupt 了！");
                }

            }
        }

        try {
            Object lock = new Object();
            Thread01 thread01 = new Thread01(lock);
            thread01.start();
            Thread.sleep(5000);
            thread01.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

//案例3
class HisList {

    private List<String> list = new ArrayList<>();

    public void add() {
        list.add("高红颜");
    }

    public int size() {
        return list.size();
    }
}

//案例3
class Demo3 {
    public static void main(String[] args) {
        class Thread001 extends Thread {
            private HisList hisList;

            private Object lock;

            public Thread001(HisList hisList, Object lock) {
                this.hisList = hisList;
                this.lock = lock;
            }

            @Override
            public void run() {
                try {
                    synchronized (lock) {
                        if (hisList.size() != 5) {
                            System.out.println("wait begin time=" + System.currentTimeMillis());
                            lock.wait();
                            System.out.println("wait end time=" + System.currentTimeMillis());
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        class Thread002 extends Thread {
            private HisList hisList;

            private Object lock;


            public Thread002(HisList hisList, Object lock) {
                this.hisList = hisList;
                this.lock = lock;
            }

            @Override
            public void run() {
                try {
                    synchronized (lock) {
                        for (int i = 0; i < 10; i++) {
                            hisList.add();
                            if (hisList.size() == 5) {
                                lock.notify();
                                System.out.println("集合数量已经达到5，发出通知");
                            }
                            System.out.println("添加了" + (i + 1) + "个元素");
                        }
                        Thread.sleep(5000);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        try {
            HisList hisList = new HisList();
            Object lock = new Object();

            Thread001 thread001 = new Thread001(hisList, lock);
            thread001.start();
            Thread.sleep(1000);
            Thread002 thread002 = new Thread002(hisList, lock);
            thread002.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


//案例2
class Demo2 {
    public static void main(String[] args) {
        class Thread1 extends Thread {
            private final Object lock;

            public Thread1(Object lock) {
                this.lock = lock;
            }

            @Override
            public void run() {
                synchronized (lock) {
                    try {
                        System.out.println("开始 wait time =" + System.currentTimeMillis());
                        lock.wait();
                        System.out.println("结束 wait time =" + System.currentTimeMillis());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        class Thread2 extends Thread {
            private final Object lock;

            public Thread2(Object lock) {
                this.lock = lock;
            }

            @Override
            public void run() {
                synchronized (lock) {
                    try {
                        System.out.println("开始 notify time =" + System.currentTimeMillis());
                        lock.notify();
                        System.out.println("结束 notify time =" + System.currentTimeMillis());
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        try {
            Object lock = new Object();
            Thread1 thread1 = new Thread1(lock);
            thread1.start();
            Thread.sleep(3000);
            Thread2 thread2 = new Thread2(lock);
            thread2.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}


//案例1
public class MyList {

    private List<String> list = new ArrayList<>();

    public void add() {
        list.add("高红颜");
    }

    public int size() {
        return list.size();
    }

}

//案例1
class Demo1 {

    public static void main(String[] args) {
        class Thread1 extends Thread {
            private MyList myList;

            public Thread1(MyList myList) {
                this.myList = myList;
            }

            @Override
            public void run() {
                try {
                    for (int i = 0; i < 10; i++) {
                        myList.add();
                        System.out.println("添加了" + (i + 1) + "个元素");
                        Thread.sleep(1000);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        class Thread2 extends Thread {
            private MyList myList;

            public Thread2(MyList myList) {
                this.myList = myList;
            }

            @Override
            public void run() {

                try {
                    while (true) {
                        if (myList.size() == 5) {
                            System.out.println("==5了，线程b要退出了！");
                            throw new InterruptedException();
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        MyList myList = new MyList();
        Thread1 thread1 = new Thread1(myList);
        thread1.setName("A");
        thread1.start();
        Thread2 thread2 = new Thread2(myList);
        thread2.setName("B");
        thread2.start();
    }
}
